{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#Requirements:\n",
    "#  - Caffe (script to install Caffe and pycaffe on a new Ubuntu 14.04 LTS x64 or Ubuntu 14.10 x64. \n",
    "#    CPU only, multi-threaded Caffe. https://stackoverflow.com/a/31396229/395857)\n",
    "#  - sudo pip install pydot\n",
    "#  - sudo apt-get install -y graphviz\n",
    "\n",
    "# Interesting resources on Caffe:\n",
    "#  - https://github.com/BVLC/caffe/tree/master/examples\n",
    "#  - http://nbviewer.ipython.org/github/joyofdata/joyofdata-articles/blob/master/deeplearning-with-caffe/Neural-Networks-with-Caffe-on-the-GPU.ipynb\n",
    "\n",
    "# Interesting resources on Iris with ANNs:\n",
    "#  - iris data set test bed: http://deeplearning4j.org/iris-flower-dataset-tutorial.html\n",
    "#  - http://se.mathworks.com/help/nnet/examples/iris-clustering.html\n",
    "#  - http://lab.fs.uni-lj.si/lasin/wp/IMIT_files/neural/doc/seminar8.pdf\n",
    "\n",
    "# Synonyms:\n",
    "#  - output = label = target\n",
    "#  - input = feature \n",
    "# '''\n",
    "\n",
    "import subprocess\n",
    "import platform\n",
    "import copy\n",
    "\n",
    "from sklearn.datasets import load_iris\n",
    "import sklearn.metrics \n",
    "import numpy as np\n",
    "from sklearn.cross_validation import StratifiedShuffleSplit\n",
    "import matplotlib.pyplot as plt\n",
    "import h5py\n",
    "import caffe\n",
    "import caffe.draw\n",
    "\n",
    "\n",
    "def load_data():\n",
    "#     '''\n",
    "#     Load Iris Data set\n",
    "#     '''\n",
    "    data = load_iris()\n",
    "    print(data.data)\n",
    "    print(data.target)\n",
    "    targets = np.zeros((len(data.target), 3))\n",
    "    for count, target in enumerate(data.target):\n",
    "        targets[count][target]= 1    \n",
    "    print(targets)\n",
    "\n",
    "    new_data = {}\n",
    "    #new_data['input'] = data.data\n",
    "    new_data['input'] = np.reshape(data.data, (150,1,1,4))\n",
    "    new_data['output'] = targets\n",
    "    #print(new_data['input'].shape)\n",
    "    #new_data['input'] = np.random.random((150, 1, 1, 4))\n",
    "    #print(new_data['input'].shape)   \n",
    "    #new_data['output'] = np.random.random_integers(0, 1, size=(150,3))    \n",
    "    #print(new_data['input'])\n",
    "\n",
    "    return new_data\n",
    "\n",
    "def save_data_as_hdf5(hdf5_data_filename, data):\n",
    "#     '''\n",
    "#     HDF5 is one of the data formats Caffe accepts\n",
    "#     '''\n",
    "    with h5py.File(hdf5_data_filename, 'w') as f:\n",
    "        f['data'] = data['input'].astype(np.float32)\n",
    "        f['label'] = data['output'].astype(np.float32)\n",
    "\n",
    "\n",
    "def train(solver_prototxt_filename):\n",
    "#     '''\n",
    "#     Train the ANN\n",
    "#     '''\n",
    "    caffe.set_mode_cpu()\n",
    "    solver = caffe.get_solver(solver_prototxt_filename)\n",
    "    solver.solve()\n",
    "\n",
    "\n",
    "def print_network_parameters(net):\n",
    "#     '''\n",
    "#     Print the parameters of the network\n",
    "#     '''\n",
    "    print(net)\n",
    "    print('net.inputs: {0}'.format(net.inputs))\n",
    "    print('net.outputs: {0}'.format(net.outputs))\n",
    "    print('net.blobs: {0}'.format(net.blobs))\n",
    "    print('net.params: {0}'.format(net.params))    \n",
    "\n",
    "def get_predicted_output(deploy_prototxt_filename, caffemodel_filename, input, net = None):\n",
    "#     '''\n",
    "#     Get the predicted output, i.e. perform a forward pass\n",
    "#     '''\n",
    "    if net is None:\n",
    "        net = caffe.Net(deploy_prototxt_filename,caffemodel_filename, caffe.TEST)\n",
    "\n",
    "    #input = np.array([[ 5.1,  3.5,  1.4,  0.2]])\n",
    "    #input = np.random.random((1, 1, 1))\n",
    "    #print(input)\n",
    "    #print(input.shape)\n",
    "    out = net.forward(data=input)\n",
    "    #print('out: {0}'.format(out))\n",
    "    return out[net.outputs[0]]\n",
    "\n",
    "\n",
    "import google.protobuf \n",
    "def print_network(prototxt_filename, caffemodel_filename):\n",
    "#     '''\n",
    "#     Draw the ANN architecture\n",
    "#     '''\n",
    "    _net = caffe.proto.caffe_pb2.NetParameter()\n",
    "    f = open(prototxt_filename)\n",
    "    google.protobuf.text_format.Merge(f.read(), _net)\n",
    "    caffe.draw.draw_net_to_file(_net, prototxt_filename + '.png' )\n",
    "    print('Draw ANN done!')\n",
    "\n",
    "\n",
    "def print_network_weights(prototxt_filename, caffemodel_filename):\n",
    "#     '''\n",
    "#     For each ANN layer, print weight heatmap and weight histogram \n",
    "#     '''\n",
    "    net = caffe.Net(prototxt_filename,caffemodel_filename, caffe.TEST)\n",
    "    for layer_name in net.params: \n",
    "        # weights heatmap \n",
    "        arr = net.params[layer_name][0].data\n",
    "        plt.clf()\n",
    "        fig = plt.figure(figsize=(10,10))\n",
    "        ax = fig.add_subplot(111)\n",
    "        cax = ax.matshow(arr, interpolation='none')\n",
    "        fig.colorbar(cax, orientation=\"horizontal\")\n",
    "        plt.savefig('{0}_weights_{1}.png'.format(caffemodel_filename, layer_name), dpi=100, format='png', bbox_inches='tight') # use format='svg' or 'pdf' for vectorial pictures\n",
    "        plt.close()\n",
    "\n",
    "        # weights histogram  \n",
    "        plt.clf()\n",
    "        plt.hist(arr.tolist(), bins=20)\n",
    "        plt.savefig('{0}_weights_hist_{1}.png'.format(caffemodel_filename, layer_name), dpi=100, format='png', bbox_inches='tight') # use format='svg' or 'pdf' for vectorial pictures\n",
    "        plt.close()\n",
    "\n",
    "\n",
    "def get_predicted_outputs(deploy_prototxt_filename, caffemodel_filename, inputs):\n",
    "#     '''\n",
    "#     Get several predicted outputs\n",
    "#     '''\n",
    "    outputs = []\n",
    "    net = caffe.Net(deploy_prototxt_filename,caffemodel_filename, caffe.TEST)\n",
    "    for input in inputs:\n",
    "        #print(input)\n",
    "        outputs.append(copy.deepcopy(get_predicted_output(deploy_prototxt_filename, caffemodel_filename, input, net)))\n",
    "    return outputs    \n",
    "\n",
    "\n",
    "def get_accuracy(true_outputs, predicted_outputs):\n",
    "#     '''\n",
    "\n",
    "#     '''\n",
    "    number_of_samples = true_outputs.shape[0]\n",
    "    number_of_outputs = true_outputs.shape[1]\n",
    "    threshold = 0.0 # 0 if SigmoidCrossEntropyLoss ; 0.5 if EuclideanLoss\n",
    "    for output_number in range(number_of_outputs):\n",
    "        predicted_output_binary = []\n",
    "        for sample_number in range(number_of_samples):\n",
    "            #print(predicted_outputs)\n",
    "            #print(predicted_outputs[sample_number][output_number])            \n",
    "            if predicted_outputs[sample_number][0][output_number] < threshold:\n",
    "                predicted_output = 0\n",
    "            else:\n",
    "                predicted_output = 1\n",
    "            predicted_output_binary.append(predicted_output)\n",
    "\n",
    "        print('accuracy: {0}'.format(sklearn.metrics.accuracy_score(true_outputs[:, output_number], predicted_output_binary)))\n",
    "        print(sklearn.metrics.confusion_matrix(true_outputs[:, output_number], predicted_output_binary))\n",
    "\n",
    "\n",
    "def main():\n",
    "#     '''\n",
    "#     This is the main function\n",
    "#     '''\n",
    "\n",
    "    # Set parameters\n",
    "    solver_prototxt_filename = 'iris_solver.prototxt'\n",
    "    train_test_prototxt_filename = 'iris_train_test.prototxt'\n",
    "    deploy_prototxt_filename  = 'iris_deploy.prototxt'\n",
    "    deploy_prototxt_filename  = 'iris_deploy.prototxt'\n",
    "    deploy_prototxt_batch2_filename  = 'iris_deploy_batchsize2.prototxt'\n",
    "    hdf5_train_data_filename = 'iris_train_data.hdf5' \n",
    "    hdf5_test_data_filename = 'iris_test_data.hdf5' \n",
    "    caffemodel_filename = 'iris__iter_5000.caffemodel' # generated by train()\n",
    "\n",
    "    # Prepare data\n",
    "    data = load_data()\n",
    "    print(data)\n",
    "    train_data = data\n",
    "    test_data = data\n",
    "    save_data_as_hdf5(hdf5_train_data_filename, data)\n",
    "    save_data_as_hdf5(hdf5_test_data_filename, data)\n",
    "\n",
    "    # Train network\n",
    "    train(solver_prototxt_filename)\n",
    "\n",
    "    # Print network\n",
    "    print_network(deploy_prototxt_filename, caffemodel_filename)\n",
    "    print_network(train_test_prototxt_filename, caffemodel_filename)\n",
    "    print_network_weights(train_test_prototxt_filename, caffemodel_filename)\n",
    "\n",
    "    # Compute performance metrics\n",
    "    #inputs = input = np.array([[[[ 5.1,  3.5,  1.4,  0.2]]],[[[ 5.9,  3. ,  5.1,  1.8]]]])\n",
    "    inputs = data['input']\n",
    "    outputs = get_predicted_outputs(deploy_prototxt_filename, caffemodel_filename, inputs)\n",
    "    get_accuracy(data['output'], outputs)\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    main()\n",
    "    #cProfile.run('main()') # if you want to do some profiling"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
